/** Java 1.3 AST Recognizer Grammar
 *
 * Author: (see java.g preamble)
 *
 * This grammar is in the PUBLIC DOMAIN
 */
class VarInfoCollector extends TreeParser;
		
options {
	importVocab = Java;		
}

{

    public void setClassInfo(ClassData cinfo) {
        _cinfo = cinfo;
	_currentCInfo = cinfo;
    }
    
    public void setFlowGraphNode(FlowGraphNode node) {
        _fgnode = node;
    }
    
    public void setStatementInfo(StatementInfo statinfo) {
        _statinfo = statinfo;
    }

    public void setVarsMap(HashMap vars) {
        _vars = vars;
    }

    private ClassData _cinfo;
    private ClassData _currentCInfo;
    private FlowGraphNode _fgnode;
    private StatementInfo _statinfo;
    private boolean _forInit;
    private boolean _forIte;

    // A tabela contendo as varinfo para cada variavel.
    private HashMap _vars;

    // Dado um nome simples, olha em todas as ST disponiveis para
    // ver se o nome e uma variavel.
    private STEntry _getSTEntry(String name) {
        STEntry entry = null;

	// Variaveis locais
	SymbolTable st = _fgnode.getSymbolTable();
	entry = (STEntry) st.find(name);
	if(entry != null) return entry;
	
	entry = _currentCInfo.getPort(name);
	if(entry != null) return entry;
	
	entry = _currentCInfo.getVariable(name);	
	if(entry != null) return entry;
	
	entry = _currentCInfo.getParameter(name);

	return entry;
    }

    private void _checkDefSite(AST expression) {
        // Pode ser uma variavel escalar
        if(expression.getType() == JavaTokenTypes.IDENT) {
	    _processDefSite(expression);
	}
	else
        if(expression.getType() == JavaTokenTypes.INDEX_OP) {
           AST v = expression.getFirstChild();
	   if(v.getType() == JavaTokenTypes.IDENT) {
	       _processDefSite(v);
	   }
        }
    }

    private void _processDefSite(AST expression) {
        String name = expression.getText();

        STEntry entry = _getSTEntry(name);
	if(entry != null) {
	    VariableInfo vinfo = (VariableInfo) _vars.get(entry);
	    if(vinfo == null) {
	        vinfo = new VariableInfo(entry, _cinfo);
	        _vars.put(entry, vinfo);
	    }

            FlowGraphNode fgnode = _fgnode;

/*	    if(_forInit) {
		// pega o no antes do for begin
		Iterator ite = fgnode.inputEdgesIterator();
		while(ite.hasNext()) {
		    FlowGraphEdge edge = (FlowGraphEdge) ite.next();
		    if(edge.isBack()) continue;
		    FlowGraphNode source = edge.getFrom();
		    if(source.getType() == FlowGraphNode.CONTINUE) continue;

	            fgnode = source;
	            break;
		}
	    }
	    else
            if(_forIte) {
		ForBeginNode fbeg = (ForBeginNode) _fgnode;
		StatementInfo info = fbeg.getIteratorInfo();
		    
		Iterator ite = fgnode.inputEdgesIterator();
		while(ite.hasNext()) {
		    FlowGraphEdge edge = (FlowGraphEdge) ite.next();
		    if(edge.isBack()) {
			fgnode = edge.getFrom();
			VariableLocation loc = new VariableLocation(fgnode, 
			    expression, _statinfo, false);
	                vinfo.addDefSite(loc);
			fgnode.addDefinedVar(entry);			    
	            }
		}
		info.addDefinedVar(vinfo, expression);

		return;
	    }*/
                

            // Mais uma location para essa varInfo
	    VariableLocation loc = new VariableLocation(fgnode, expression, 
	        _statinfo, false);
	    vinfo.addDefSite(loc);

            // Adiciona a info para o algoritmo de colocacao de phi node
	    fgnode.addDefinedVar(entry);
		
	    // Adiciona essa var info ao StatementInfo do respectivo no
	    switch(fgnode.getType()) {

		case FlowGraphNode.BASIC_BLOCK: {
		    BasicBlockNode bb = (BasicBlockNode) fgnode;
		    _statinfo.addDefinedVar(vinfo, expression);
		} break;

		case FlowGraphNode.FOR_BEGIN: {
		   ForBeginNode fbeg = (ForBeginNode) fgnode;
		   if(_forInit) {
		       // isso nunca pode dar...
		       StatementInfo info = fbeg.getInitInfo();
		       info.addDefinedVar(vinfo, expression);
		   }
		   else {
		       // se nao e init e cond.
		       StatementInfo info = fbeg.getConditionInfo();
	               info.addDefinedVar(vinfo, expression);
		   }
	        } break;

		default: {
		    // teoricamente nao pode dar null
		    StatementInfo info = fgnode.getStatementInfo();
		    info.addDefinedVar(vinfo, expression);
		}
	    }
	}
    }

    private void _checkUseSite(AST expression) {
        if(expression == null) return;

        if(expression.getType() == JavaTokenTypes.IDENT) {
	    _processUseSite(expression);
	}
	else
	if(expression.getType() == JavaTokenTypes.INDEX_OP) {
           AST v = expression.getFirstChild();
	   if(v.getType() == JavaTokenTypes.IDENT) {
	       _processUseSite(v);
	   }
        }
    }

    private void _processUseSite(AST expression) {
        String name = expression.getText();

	STEntry entry = _getSTEntry(name);
	if(entry != null) {
	    VariableInfo vinfo = (VariableInfo) _vars.get(entry);
	    if(vinfo == null) {
	        vinfo = new VariableInfo(entry, _cinfo);
		_vars.put(entry, vinfo);
	    }

	    FlowGraphNode fgnode = _fgnode;
/*
	    if(_forInit) {
		Iterator ite = fgnode.inputEdgesIterator();
		while(ite.hasNext()) {
		    FlowGraphEdge edge = (FlowGraphEdge) ite.next();
		    if(edge.isBack()) continue;
		    FlowGraphNode source = edge.getFrom();
	            if(source.getType() == FlowGraphNode.CONTINUE) continue;

		    fgnode = source;

		    break;
		}
            }
	    else
	    if(_forIte) {
		ForBeginNode fbeg = (ForBeginNode) _fgnode;
		StatementInfo info = fbeg.getIteratorInfo();

		Iterator ite = fgnode.inputEdgesIterator();
		while(ite.hasNext()) {
		    FlowGraphEdge edge = (FlowGraphEdge) ite.next();
		    if(edge.isBack()) {
			fgnode = edge.getFrom();
			VariableLocation loc = new VariableLocation(fgnode, 
			    expression, _statinfo, false);
	                vinfo.addUseSite(loc);
			
			// Adiciona no statementInfo
	            }
		}
		info.addUsedVar(vinfo, expression);

		return;
	    } */

	    VariableLocation loc = new VariableLocation(fgnode, 
	        expression, _statinfo, false);
	    vinfo.addUseSite(loc);
		
            // Adiciona essa var info ao StatementInfo do respectivo no
	    switch(fgnode.getType()) {

	        case FlowGraphNode.BASIC_BLOCK: {
	            BasicBlockNode bb = (BasicBlockNode) _fgnode;
		    _statinfo.addUsedVar(vinfo, expression);
	        } break;
		    
                case FlowGraphNode.FOR_BEGIN: {
	            ForBeginNode fbeg = (ForBeginNode) _fgnode;
		    /*if(_forInit) {
		        StatementInfo info = fbeg.getInitInfo();
		        info.addUsedVar(vinfo, expression);		       
		    }
		    else {*/
		        // se nao e init e cond.
		        StatementInfo info = fbeg.getConditionInfo();
		        info.addUsedVar(vinfo, expression);
		    // }
	        } break;
		    
                default: {
	    	    // teoricamente nao pode dar null
		    StatementInfo info = fgnode.getStatementInfo();
		    info.addUsedVar(vinfo, expression);
	        }
	    }
	}
    }
}

statement:

	stat

	| methodcall

	| ctorCall

	| #(FOR_INIT { _forInit = true; } (variableDef | elist)?) { _forInit = false; }

	| #(FOR_CONDITION (expression)?)

	| #(FOR_ITERATOR { _forIte = true; } (elist)?  { _forIte = false; } )
	
/*	| #( "catch" parameterDef2 slist ) */
	
	| parameterDef2  /* para o catch */
	
	;


typeDefinition
	:	#(CLASS_DEF modifiers IDENT extendsClause implementsClause objBlock )
	|	#(INTERFACE_DEF modifiers IDENT extendsClause interfaceBlock )
	;

typeSpec
	:	#(TYPE typeSpecArray)
	;

typeSpecArray
	:	#( ARRAY_DECLARATOR typeSpecArray )
	|	type
	;

type:	identifier
	|	builtInType
	;

builtInType
    :   "void"
    |   "boolean"
    |   "byte"
    |   "char"
    |   "short"
    |   "int"
    |   "float"
    |   "long"
    |   "double"
    ;

modifiers
	:	#( MODIFIERS (modifier)* )
	;

modifier
    :   "private"
    |   "public"
    |   "protected"
    |   "static"
    |   "transient"
    |   "final"
    |   "abstract"
    |   "native"
    |   "threadsafe"
    |   "synchronized"
    |   "const"
    |   "volatile"
	|	"strictfp"
    ;

extendsClause
	:	#(EXTENDS_CLAUSE (identifier)* )
	;

implementsClause
	:	#(IMPLEMENTS_CLAUSE (identifier)* )
	;


interfaceBlock
	:	#(	OBJBLOCK
			(	methodDecl
			|	variableDef
			)*
		)
	;
	
objBlock
	:	#(	OBJBLOCK
			(	ctorDef
			|	methodDef
			|	variableDef
			|	typeDefinition
			|	#(STATIC_INIT slist)
			|	#(INSTANCE_INIT slist)
			)*
		)
	;

ctorDef
	:	#(CTOR_DEF modifiers methodHead ctorSList)
	;

methodDecl
	:	#(METHOD_DEF modifiers typeSpec methodHead)
	;

methodDef
	:	#(METHOD_DEF modifiers typeSpec methodHead (slist)?)
	;

variableDef
	:	#(VARIABLE_DEF 
	             modifiers 
		     typeSpec 
		     n1:variableDeclarator { _checkDefSite(n1); }
		     n2:varInitializer { _checkUseSite(n2); } )
	;

parameterDef
	:	#(PARAMETER_DEF modifiers typeSpec IDENT )
	;

parameterDef2
	:	#(PARAMETER_DEF modifiers typeSpec n1:IDENT { _checkDefSite(n1); } )
	;


objectinitializer
	:	#(INSTANCE_INIT slist)
	;

variableDeclarator
	:	IDENT
	|	LBRACK n1:variableDeclarator { _checkDefSite(n1); }
	;

varInitializer
	:	#(ASSIGN initializer)
	|
	;

initializer
	:	expression
	|	arrayInitializer
	;

arrayInitializer
	:	#(ARRAY_INIT (initializer)*)
	;

methodHead
	:	IDENT #( PARAMETERS (parameterDef)* ) (throwsClause)?
	;

throwsClause
	:	#( "throws" (identifier)* )
	;

identifier   
	:	IDENT  
	|	#( DOT identifier IDENT ) 
	;

identifierStar
	:	IDENT
	|	#( DOT identifier (STAR|IDENT) )
	;

ctorSList
	:	#( SLIST (ctorCall)? (stat)* )
	;

slist
	:	#( SLIST (stat)* )
	;

stat:	typeDefinition
	|	variableDef
	|	expression
	|	#(LABELED_STAT IDENT stat)
	|	#("if" expression stat (stat)? )
	|	#(	"for"
			#(FOR_INIT (variableDef | elist)?)
			#(FOR_CONDITION (expression)?)
			#(FOR_ITERATOR (elist)?)
			stat
		)
	|	#("while" expression stat)
	|	#("do" stat expression)
	|	#("break" (IDENT)? )
	|	#("continue" (IDENT)? )
	|	#("return" (expression)? )
	|	#("switch" expression (caseGroup)*)
	|	#("throw" expression)
	|	#("synchronized" expression stat)
	|	tryBlock
	|	slist // nested SLIST
	|	EMPTY_STAT
	;

caseGroup
	:	#(CASE_GROUP (#("case" expression) | "default")+ slist)
	;

tryBlock
	:	#( "try" slist (handler)* (#("finally" slist))? )
	;

handler
	:	#( "catch" parameterDef slist )
	;

elist
	:	#( ELIST (expression)* )
	;

expression
	:	#(EXPR n1:expr { _checkUseSite(n1); } )
	;

expr:	
		// aqui, pra ser def, tem que ter outra expr. 
		// use pode tar a var direto...
		#(QUESTION nq1:expr { _checkUseSite(nq1); } 
		           nq2:expr { _checkUseSite(nq2); }
			   nq3:expr { _checkUseSite(nq3); } )

	|	#(asm:ASSIGN 
	             n1:expr { _checkDefSite(n1); } 
		     n1b:expr { _checkUseSite(n1b); } )
	
	|	#(PLUS_ASSIGN 
	             n2:expr { _checkDefSite(n2); }
		     n2b:expr { _checkUseSite(n2b); } )
		     
	|	#(MINUS_ASSIGN 
	             n3:expr { _checkDefSite(n3); }
		     n3b:expr { _checkUseSite(n3b); } )
		     
	|	#(STAR_ASSIGN 
	             n4:expr { _checkDefSite(n4); }
		     n4b:expr { _checkUseSite(n4b); } )
	
	|	#(DIV_ASSIGN 
	             n5:expr { _checkDefSite(n5); }
		     n5b:expr { _checkUseSite(n5b); } )
	
	|	#(MOD_ASSIGN 
	             n6:expr { _checkDefSite(n6); }
		     n6b:expr { _checkUseSite(n6b); } )
	
	|	#(SR_ASSIGN 
	             n7:expr { _checkDefSite(n7); }
		     n7b:expr { _checkUseSite(n7b); } )
	
	|	#(BSR_ASSIGN 
	             n8:expr { _checkDefSite(n8); }
		     n8b:expr { _checkUseSite(n8b);} )
	
	|	#(SL_ASSIGN 
	             n9:expr { _checkDefSite(n9); }
		     n9b:expr { _checkUseSite(n9b); } )
	
	|	#(BAND_ASSIGN 
	             n10:expr { _checkDefSite(n10); }
		     n10b:expr { _checkUseSite(n10b); } )
	
	|	#(BXOR_ASSIGN 
	             n11:expr { _checkDefSite(n11); }
		     n11b:expr { _checkUseSite(n11b); } )
	
	|	#(BOR_ASSIGN 
	             n12:expr { _checkDefSite(n12); }
		     n12b:expr { _checkUseSite(n12b);} )
	
	|	#(LOR n13:expr { _checkUseSite(n13); } n14:expr { _checkUseSite(n14); })
	
	|	#(LAND n15:expr { _checkUseSite(n15); } n16:expr { _checkUseSite(n16); } )
	
	|	#(BOR n17:expr { _checkUseSite(n17); }  n18:expr { _checkUseSite(n18); } )
	
	|	#(BXOR n19:expr { _checkUseSite(n19); } n20:expr { _checkUseSite(n20); } )
	
	|	#(BAND n21:expr { _checkUseSite(n21); } n22:expr { _checkUseSite(n22); } )
	
	|	#(NOT_EQUAL n23:expr { _checkUseSite(n23); } n24:expr { _checkUseSite(n24); } )
	
	|	#(EQUAL n25:expr { _checkUseSite(n25); } n26:expr { _checkUseSite(n26); } )
	
	|	#(LT n27:expr { _checkUseSite(n27); } n28:expr { _checkUseSite(n28);} )
	
	|	#(GT n29:expr { _checkUseSite(n29); } n30:expr { _checkUseSite(n30); } )
	
	|	#(LE n31:expr { _checkUseSite(n31); } n32:expr { _checkUseSite(n32); })
	
	|	#(GE n33:expr { _checkUseSite(n33); } n34:expr { _checkUseSite(n34); })
	
	|	#(SL n35:expr { _checkUseSite(n35); } n36:expr { _checkUseSite(n36); } )
	
	|	#(SR n37:expr { _checkUseSite(n37); } n38:expr { _checkUseSite(n38); } )
	
	|	#(BSR n39:expr { _checkUseSite(n39); }  n40:expr { _checkUseSite(n40); } )
	
	|	#(PLUS np1:expr {_checkUseSite(np1);} np2:expr {_checkUseSite(np2); })
	
	|	#(MINUS n41:expr { _checkUseSite(n41); }  n42:expr { _checkUseSite(n42); } )
	
	|	#(DIV n43:expr { _checkUseSite(n43); } n44:expr { _checkUseSite(n44); })
	
	|	#(MOD n45:expr{ _checkUseSite(n45); } n46:expr { _checkUseSite(n46); })
	
	|	#(STAR n47:expr { _checkUseSite(n47); } n48:expr { _checkUseSite(n48); } )


		// Aqui a ordem nao importa. As chains e os nomes serao escritos
		// no rename do SSA.
	|	#(INC n49:expr  { _checkDefSite(n49); _checkUseSite(n49); } )

	|	#(DEC n50:expr { _checkDefSite(n50); _checkUseSite(n50); } )

	|	#(POST_INC n51:expr { _checkUseSite(n51); _checkDefSite(n51); } )

	|	#(POST_DEC n52:expr { _checkUseSite(n52); _checkDefSite(n52); } )

	|	#(BNOT n53:expr { _checkUseSite(n53); } )
	
	|	#(LNOT n54:expr { _checkUseSite(n54); } )

	|	#("instanceof" n55:expr { _checkUseSite(n55); } expr)

	|	#(UNARY_MINUS n56:expr { _checkUseSite(n56); })

	|	#(UNARY_PLUS n57:expr { _checkUseSite(n57); } )

	|	primaryExpression
	;

primaryExpression
    :   n1:IDENT

    |   #(	DOT
			(	n2:expr { _checkUseSite(n2); }
				
				(	IDENT // aqui nao tem nada, pois ta no meio...

				|	arrayIndex1

				|	"this"

				|	"class"

				|	#( "new" IDENT elist )

				|   "super" {
				       _currentCInfo = _currentCInfo.getSuperClass();
				    }

				)

			|	#(ARRAY_DECLARATOR typeSpecArray)

			|	builtInType ("class")?

			)
		) { _currentCInfo = _cinfo; }
	
	|	arrayIndex2

	|	methodcall

	|	#(TYPECAST typeSpec n3:expr { _checkUseSite(n3); } )

	|   newExpression
	
	|   constant

    |   "super"

    |   "true"

    |   "false"

    |   "this"

    |   "null"

	|	typeSpec // type name used with instanceof
	;

methodcall:
	#(METHOD_CALL n1:primaryExpression { _checkUseSite(n1); } elist)
	;

ctorCall
	:	#( CTOR_CALL elist )
	|	#( SUPER_CTOR_CALL
			(	elist
			|	n1:primaryExpression { _checkUseSite(n1); } elist
			)
		 )
	;

arrayIndex1
	:	#(INDEX_OP primaryExpression expression)
	;

arrayIndex2
	:	#(INDEX_OP
	              n2:primaryExpression { /*_checkUseSite(n2); */ }
	              n1:expression
		 )
	;

constant
    :   NUM_INT
    |   CHAR_LITERAL
    |   STRING_LITERAL
    |   NUM_FLOAT
    |   NUM_DOUBLE
    |   NUM_LONG
    ;

newExpression
	:	#(	"new" type
			(	newArrayDeclarator (arrayInitializer)?
			|	elist (objBlock)?
			)
		)
			
	;

newArrayDeclarator
	:	#( ARRAY_DECLARATOR (newArrayDeclarator)? (expression)? )
	;
